// Author	: damphat@gmail.com
// Created	: 01/12/2008

#pragma once
#include "vn/NetCommon.h"
#include <assert.h>


NET_BEGIN

// Address of IPv4 or IPv6
// change to IPv4 only for easier port
class IPAddress {
public:
	// IPv4 static constants
    static const IPAddress Any;
    static const IPAddress Broadcast;
    static const IPAddress Loopback;
    static const IPAddress None;

public:
	// Example: IPAddress(0x00FFFFFF) equivalent to "255.255.255.0"
	explicit IPAddress(ULong ipNum = 0) {
		m_type = AF_INET;
		m_data.v4.S_un.S_addr = ipNum;
	}

	// Example: IPAddress(127,0,0,1) equivalent to "127.0.0.1"
	IPAddress(UChar b1, UChar b2, UChar b3, UChar b4) {
		m_type = AF_INET;
		m_data.v4.S_un.S_un_b.s_b1 = b1;
		m_data.v4.S_un.S_un_b.s_b2 = b2;
		m_data.v4.S_un.S_un_b.s_b3 = b3;
		m_data.v4.S_un.S_un_b.s_b4 = b4;
	}

	// Initialize from network address buffer
	IPAddress(const Buf8 *addrBuf, int len) {
		CHECK_ARG_NOT_NULL(addrBuf);
		CHECK_ARG((len == 4) || (len == 16));	// IPv4 || IPv6

		if(len == 4) {
			m_type = AF_INET;
			m_data.v4.S_un.S_addr = *((ULong*)addrBuf);
		}
		else if(len == 16) {
			// remove to compile with mingw5
			// m_type = AF_INET6;
			// for(int i=0; i < 16; i++)
			//	m_data.v6.u.Byte[i] = addrBuf[i];
		}
	}

	// Example: IPAddress ip =  IPAddress::Parse("10.0.0.5")
	// For domain name, you must use Dns::GetHostEntry
	static IPAddress Parse(const String &ipStr) {
		// currently: V4 only
		CHECK_ARG_NOT_NULL(ipStr.C_STR());
		StringA ansi = ipStr;
		return IPAddress(inet_addr(ansi.C_STR()));
	}

	int GetAddressFamily() const {
		return m_type;
	}

	const Buf8* GetData() const {
		return (const Buf8 *)&m_data;
	}

	Buf8* GetData() {
		return (Buf8 *)&m_data;
	}

	int GetLength() const {
		switch(m_type) {
		case AF_INET:
			return sizeof(m_data.v4);
		case AF_INET6:
			return sizeof(m_data.v4);
		}

		assert(false);
		return -1;
	}

	bool operator == (const IPAddress &ref) const {
		if(m_type != ref.m_type)
			return false;

		switch(m_type) {
		case AF_INET:
			return m_data.v4.S_un.S_addr == ref.m_data.v4.S_un.S_addr;
		// mingw
		// case AF_INET6:
		//	return 0 == memcmp(m_data.v6.u.Byte, ref.m_data.v6.u.Byte, 16);
		}

		assert(false); // never happend require!
		return false;
	}

	bool operator != (const IPAddress &ref) const {
		return !( (*this) == ref );
	}

	virtual ~IPAddress() {
	}

private:
	int	m_type;
	union {
		in_addr		v4;
		// mingw
		// in_addr6	v6;
	} m_data;
};

NET_END

// problems:
//	should we expose data type from winsock2.h like u_long,... ?
//	IPv4 & IPv6 in 1 class?
//		in_addr		(4  bytes)
//		in6_addr	(16 bytes)
//
//	members access
//		read
//		write
//
//	printing
//
//	compare?
//
//	virtual destructor

//	should we use uppercase or lowercase: in_addr or IN_ADDR?
//	address address-family, and return type of GetData() should be Buf8 *?
//  Remove GetData and allow IPEndPoint to access directly to IPAddress data? seem good because it minimize interface?
